<?php

namespace App\Model;

use App\Utility\Pool\MysqlObject;
use App\Utility\Pool\MysqlPool;
use EasySwoole\Component\Pool\PoolManager;
use EasySwoole\EasySwoole\Config;
use EasySwoole\EasySwoole\Logger;
use EasySwoole\Spl\SplBean;

class BaseModel{
    protected $db;
    protected $table;
    protected $casts = [];//字段转化
    protected $fillable = [];//字段过滤
    protected $hidden = [];//字段隐藏
    protected $perPage = 10;
    protected $columns;
    protected $limit = null;

    public function __construct(){
        $db = PoolManager::getInstance()
            ->getPool(MysqlPool::class)
            ->getObj(Config::getInstance()->getConf('MYSQL.POOL_TIME_OUT'));
        // 请注意判断类型 避免拿到非期望的对象产生误操作
        if($db instanceof MysqlObject) {
            $this->db = $db;
        }else{
            Logger::getInstance()->log('mysql pool is empty','database');
            throw new \Exception('mysql pool is empty');
        }
    }

    public function __destruct(){
        // 请注意判断类型 避免将不属于该链接池的对象回收到池中
        if($this->db instanceof MysqlObject) {
            PoolManager::getInstance()->getPool(MysqlPool::class)->recycleObj($this->db);
        }
    }

    /**
     * 根据主键ID查询一条数据
     * @param $id
     * @return \EasySwoole\Mysqli\Mysqli|mixed|null
     * @throws \EasySwoole\Mysqli\Exceptions\ConnectFail
     * @throws \EasySwoole\Mysqli\Exceptions\PrepareQueryFail
     * @throws \Throwable
     */
    public function find($id) {
        $data = $this->db->where($this->table.'.id', $id)->getOne($this->table,$this->columns);
        $data = $this->getAttribute($data);
        return $data;
    }

    /**
     * 设置字段
     * @param $data
     * @return mixed
     */
    protected function setAttribute($data){
        foreach($data as $key=>&$value){
            if(count($this->casts) && in_array($key,$this->casts)) $value = json_encode($value);
        }
        return $data;
    }

    /**
     * 转化字段
     * @param $data
     * @return mixed
     */
    protected function getAttribute($data){
        if(!$data) return $data;
        foreach($data as $key=>&$value){
            if(count($this->casts) && in_array($key,$this->casts)) $value = json_decode($value);
        }
        return $data;
    }

    /**
     * 新增
     * @param $data
     * @return bool|int
     * @throws \EasySwoole\Mysqli\Exceptions\ConnectFail
     * @throws \EasySwoole\Mysqli\Exceptions\PrepareQueryFail
     * @throws \Throwable
     */
    public function create($data) {
        $data = $this->fillableAttribute($data);
        $data = $this->setAttribute($data);
        return $this->db->insert($this->table,$data);
    }

    /**
     * 更新
     * @param $data
     * @return mixed
     * @throws \EasySwoole\Mysqli\Exceptions\ConnectFail
     * @throws \EasySwoole\Mysqli\Exceptions\PrepareQueryFail
     * @throws \Throwable
     */
    public function update($data){
        $data = $this->fillableAttribute($data);
        $data = $this->setAttribute($data);
        return $this->db->where('id', $data['id'])->update($this->table, $data);
    }

    /**
     * 过滤字段
     * @param $data
     * @return mixed
     */
    protected function fillableAttribute($data){
        foreach($data as $key=>&$value){
            if($key == 'id') continue;//注意更新
            if(count($this->fillable) && !in_array($key,$this->fillable)) unset($data[$key]);
        }
        return $data;
    }

    /**
     * 自增
     * @param $id
     * @param $field
     * @param int $number
     * @return mixed
     * @throws \EasySwoole\Mysqli\Exceptions\ConnectFail
     * @throws \EasySwoole\Mysqli\Exceptions\PrepareQueryFail
     * @throws \Throwable
     */
    public function inc($id,$field,$number = 1){
        return $this->update(['id' => $id ,$field => $this->db->inc($number)]);
    }



    /**
     * 删除
     * @param $id
     * @return bool|null
     * @throws \EasySwoole\Mysqli\Exceptions\ConnectFail
     * @throws \EasySwoole\Mysqli\Exceptions\PrepareQueryFail
     * @throws \Throwable
     */
    public function destroy($id){
        return $this->db->where('id', $id)->delete($this->table, 1);
    }

    /**
     * 分页大小
     * @return int
     */
    protected function getPerPage(){
        return $this->perPage;
    }

    /**
     * 分页
     * @param int $page 当前页码
     * @param null $perPage 每页数量
     * @return array
     * @throws \EasySwoole\Mysqli\Exceptions\ConnectFail
     * @throws \EasySwoole\Mysqli\Exceptions\Option
     * @throws \EasySwoole\Mysqli\Exceptions\PrepareQueryFail
     * @throws \Throwable
     */
    public function paginate(int $page = 1, $perPage = null){
        $perPage = $perPage ?: $this->getPerPage();
        $data = $this->db->withTotalCount()->get($this->table, [($page - 1) * $perPage, $perPage], $this->columns);
        $total = $this->db->getTotalCount();
        return [
            'data' => $data,
            'total' => $total,
            'last_page'=>ceil($total/$perPage),
            'per_page'=>$perPage,
            'current_page'=>$page,
        ];
    }

    /**
     * 设置查询的字段
     * @param string $columns
     * @return $this
     */
    public function select($columns = '*'){
        $this->columns = $columns;
        return $this;
    }

    /**
     * 设置排序
     * @param $field
     * @param string $desc
     * @return $this
     * @throws \EasySwoole\Mysqli\Exceptions\OrderByFail
     */
    public function orderBy($field,$desc='DESC'){
        $this->db->orderBy($field,$desc);
        return $this;
    }

    /**
     * 获取x表所有数据
     * @return MysqlObject|mixed
     * @throws \EasySwoole\Mysqli\Exceptions\ConnectFail
     * @throws \EasySwoole\Mysqli\Exceptions\PrepareQueryFail
     * @throws \Throwable
     */
    public function getAll(){
        return $this->db->get($this->table,$this->limit,$this->columns);
    }

    /**
     * 获取单条数据
     * @return \EasySwoole\Mysqli\Mysqli|mixed|null
     * @throws \EasySwoole\Mysqli\Exceptions\ConnectFail
     * @throws \EasySwoole\Mysqli\Exceptions\PrepareQueryFail
     * @throws \Throwable
     */
    public function first(){
        $data = $this->db->getOne($this->table,$this->columns);
        return $this->getAttribute($data);
    }

    /**
     * 随便获取一条数据
     * @return $this
     * @throws \EasySwoole\Mysqli\Exceptions\OrderByFail
     */
    public function inRandomOrder(){
        $this->db->orderBy('RAND()');
        return $this;
    }

    /**
     * 设置查询数量
     * @param int $limit
     * @return $this
     */
    public function take($limit = 1){
         $this->limit = $limit;
        return $this;
    }

    /**
     * 设置条件
     * @param $column
     * @param null $value
     * @param string $operator
     * @return $this
     */
    public function where($column, $value = null, $operator = '=', $cond = 'AND'){
        $this->db->where($column, $value, $operator, $cond);
        return $this;
    }

    /**
     * 设置日期条件
     * @param $column
     * @param null $value
     * @param string $operator
     * @return $this
     */
    public function whereDate($column, $value = null, $operator = '='){
        $this->db->where('DATE_FORMAT(`'.$column.'`,"%Y-%m-%d")', $value, $operator);
        return $this;
    }

    /**
     * 统计结果
     * @return int
     * @throws \EasySwoole\Mysqli\Exceptions\ConnectFail
     * @throws \EasySwoole\Mysqli\Exceptions\Option
     * @throws \EasySwoole\Mysqli\Exceptions\PrepareQueryFail
     * @throws \Throwable
     */
    public function count(){
        $this->db->withTotalCount()->get($this->table);
        return $this->db->getTotalCount();
    }

    /**
     * 和
     * @param $column
     * @return mixed
     * @throws \EasySwoole\Mysqli\Exceptions\ConnectFail
     * @throws \EasySwoole\Mysqli\Exceptions\PrepareQueryFail
     * @throws \Throwable
     */
    public function sum($column){
        return $this->db->sum($this->table,$column);
    }

    /**
     * 设置分组
     * @param $field
     * @return $this
     */
    public function groupBy($field){
        $this->db->groupBy($field);
        return $this;
    }

    /**
     * 删除
     * @return bool|null
     * @throws \EasySwoole\Mysqli\Exceptions\ConnectFail
     * @throws \EasySwoole\Mysqli\Exceptions\PrepareQueryFail
     * @throws \Throwable
     */
    public function delete(){
        return $this->db->delete($this->table);
    }

    /**
     * 获取最近一条sql语句
     * @return mixed
     */
    public function sql(){
        return $this->db->getLastQuery();
    }

}
